package smallfrench.moteur.noeuds;

import java.util.HashMap;
import java.util.Map.Entry;

import smallfrench.moteur.biblio.AClasse;
import smallfrench.moteur.biblio.RClasse;
import smallfrench.moteur.biblio.err.ErreurClasse;
import smallfrench.moteur.interp.Portee;
import smallfrench.moteur.interp.RObjet;
import smallfrench.moteur.lexer.lexemes.Lexeme;

public class NClasse extends Noeud {

	/**
	 * Le nom de la classe (qui sera aussi le nom de la variable de la classe)
	 */
	private final String nom;

	/**
	 * Les noeuds des références des classes parentes.
	 */
	private final Noeud[] noeudsParentes;

	/**
	 * Le constructeur. Peut être <code>null</code>.
	 */
	private final NBlocFonction noeudConstructeur;


	private final HashMap<String, NBlocFonction> noeudsMethodes;

	//-----------------------------------------------------------------

	public NClasse(Lexeme t,
			String nom,
			Noeud[] noeudsParentes,
			NBlocFonction noeudConstructeur,
			HashMap<String, NBlocFonction> noeudsMethodes) {

		super(t);

		this.nom = nom;
		this.noeudsParentes = noeudsParentes;
		this.noeudConstructeur = noeudConstructeur;
		this.noeudsMethodes = noeudsMethodes;
	}

	//-----------------------------------------------------------------

	@Override
	public RObjet evaluer(Portee portee) {

		// On commence par récupérer les classes parentes

		RClasse[] parentes;

		if (noeudsParentes == null || noeudsParentes.length == 0)
			parentes = new RClasse[] {};
		else {

			parentes = new RClasse[noeudsParentes.length];

			for (int i = 0; i < parentes.length; i++) {

				RObjet o = noeudsParentes[i].evaluer(portee);

				if (! (o instanceof RClasse))

					throw ErreurClasse.nouvelleException(
							noeudsParentes[i].lexeme.debut,
							o, "Classe");

				parentes[i] = (RClasse) o;
			}
		}

		// On récupère les méthodes

		HashMap<String, RObjet> methodes =
				new HashMap<String, RObjet>();

		for (Entry<String, NBlocFonction> i : noeudsMethodes.entrySet()) {
			methodes.put(i.getKey(), i.getValue().evaluer(portee));
		}

		RObjet constructeur = noeudConstructeur == null ?
				null : noeudConstructeur.evaluer(portee);

		// On crée la classe
		AClasse classe = new AClasse(
				nom,
				parentes,
				constructeur,
				methodes,
				new HashMap<String, RObjet>(),
				false);

		portee.ajouterVariable(nom, classe);
		return classe;
	}

	@Override
	public String toString() {
		return "classe " + nom;
	}

}
